/**
 * Project trac. Copyright May 30, 2012.
 */
package org.mediahouse.trac.model;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import com.google.appengine.api.datastore.DatastoreFailureException;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;

/**
 * @author Tin
 * @version %I%, %G%
 * 
 */
public abstract class DeliverableServiceFactory {

    /**
     * @return an available instance of DeliverableService with default config.
     * @see org.mediahouse.trac.model.DeliverableService
     */
    public static final org.mediahouse.trac.model.DeliverableService getDeliverableService() {
        return newDeliverableServiceWithDefaults();
    }

    /**
     * @return a new DeliverableService instance with system default
     *         configuration settings.
     * @see org.mediahouse.trac.model.DeliverableService
     */
    private static org.mediahouse.trac.model.DeliverableService newDeliverableServiceWithDefaults() {
        return new DeliverableService();
    }

    /**
     * @author Tin
     * @version %I%, %G%
     * @see org.mediahouse.trac.model.DeliverableService
     */
    private static final class DeliverableService implements org.mediahouse.trac.model.DeliverableService {

        private static final String KIND = "Deliverable";
        private final DatastoreService datastore;

        /**
         * Constructs a new DeliverableService instance with system default
         * configuration settings.
         */
        public DeliverableService() {
            datastore = DatastoreServiceFactory.getDatastoreService();
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.mediahouse.trac.model.DeliverableService#getDeliverables(java
         *      .lang.String[])
         */
        @Override
        public List<org.mediahouse.trac.model.Deliverable> getDeliverables(final String... filters) {
            final PreparedQuery query = datastore.prepare(new Query(KIND));
            final Iterator<Entity> iterator = query.asIterator();
            final List<org.mediahouse.trac.model.Deliverable> deliverables = new ArrayList<org.mediahouse.trac.model.Deliverable>(query.countEntities(FetchOptions.Builder.withDefaults()));
            while (iterator.hasNext()) {
                deliverables.add(this.toDeliverable(iterator.next()));
            }
            return deliverables;
        }

        /**
         * @param deliverable
         *            the entity to convert into a {@code Deliverable} wrapper.
         * @return a deep copy of the given entity as a {@code Deliverable}
         *         instance, provided that the given entity is compatible with
         *         the {@link org.mediahouse.trac.model.Deliverable}
         *         specification
         * @throws UnsupportedOperationException
         *             Not implemented yet.
         */
        protected org.mediahouse.trac.model.Deliverable toDeliverable(final Entity deliverable) {
            if (KIND.equals(deliverable.getKind())) {

            }
            throw new UnsupportedOperationException("Not implemented yet.");
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.mediahouse.trac.model.DeliverableService#newDeliverable(java.lang.String,
         *      int, java.lang.Object, java.lang.Object, java.util.Date,
         *      java.lang.String)
         * @throws java.lang.IllegalArgumentException
         *             If any incorrect arguments were specified.
         * @throws java.util.ConcurrentModificationException
         *             If the deliverables datastore was modified concurrently.
         * @throws DatastoreFailureException
         *             If any other datastore error occurs.
         */
        @Override
        public String newDeliverable(final String name,
                final int status,
                final Object owner,
                final Object qa,
                final Date due,
                final String textNotes) {

            if (name == null || owner == null || qa == null || due == null
                    || textNotes == null) throw new IllegalArgumentException("The deliverable cannot be constructed as one or more required properties are missing.");
            else if (!allowed(name, status, owner, qa, due, textNotes)) throw new IllegalArgumentException("Cannot construct a deliverable with the given properties.");
            final Deliverable d = new Deliverable(name, status, owner, qa, due, textNotes);
            final Key k = datastore.put(d.getEntity());
            if (k.isComplete()) d.setPut(true);
            return KeyFactory.keyToString(k);
        }

        /**
         * @param name
         *            the name of this deliverable.
         * @param status
         *            the status of this deliverable.
         * @param owner
         *            the reference ID of the owner of this deliverable.
         * @param qa
         *            the reference ID of the designated Quality Assurance of
         *            this deliverable.
         * @param due
         *            the due date of this deliverable.
         * @param textNotes
         *            the textual notes included with this deliverable.
         * @return {@code true} if and only if the given arguments are necessary
         *         and sufficient in order to construct a valid
         *         {@code Deliverable} instance; {@code false} otherwise
         */
        private boolean allowed(final String name,
                final int status,
                final Object owner,
                final Object qa,
                final Date due,
                final String textNotes) {

            final boolean nameOk = name != null && name.length() > 0;
            final boolean statusOk = status > 0;
            final boolean ownerOk = owner != null;
            final boolean qaOk = qa != null;
            final boolean dueOk = due != null;
            final boolean textNotesOk = textNotes != null;
            return (nameOk && statusOk && ownerOk && qaOk && dueOk && textNotesOk);
        }

        /**
         * @author Tin
         * @version %I%, %G%
         * 
         */
        private class Deliverable implements org.mediahouse.trac.model.Deliverable {

            private final Entity entity;
            private boolean put;

            /**
             * @param name
             *            the name of this deliverable.
             * @param status
             *            the status of this deliverable.
             * @param owner
             *            the reference ID of the owner of this deliverable.
             * @param qa
             *            the reference ID of the designated Quality Assurance
             *            of this deliverable.
             * @param due
             *            the due date of this deliverable.
             * @param textNotes
             *            the textual notes included with this deliverable.
             */
            public Deliverable(final String name,
                    final int status,
                    final Object owner,
                    final Object qa,
                    final Date due,
                    final String textNotes) {
                this.entity = new Entity(KIND);
                this.entity.setProperty("name", name);
                this.entity.setProperty("status", status);
                this.entity.setProperty("owner", owner);
                this.entity.setProperty("qa", qa);
                this.entity.setProperty("due", due);
                this.entity.setProperty("textNotes", textNotes);
                construct();
            }

            /**
             * Required construction procedures.
             */
            private void construct() {
                this.put = false;
            }

            /**
             * @return the underlying datastore {@link Entity} of this
             *         Deliverable instance.
             */
            protected Entity getEntity() {
                return entity;
            }

            /**
             * @param value
             *            the boolean value indicating whether this deliverable
             *            has been put to the datastore.
             */
            protected void setPut(final boolean value) {
                if (this.put != true) this.put = value;
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Deliverable#isPut()
             */
            @Override
            public boolean isPut() {
                return this.put;
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Deliverable#getId()
             * @throws UnsupportedOperationException
             *             Not implemented yet.
             */
            @Override
            public String getId() {
                // TODO implement for support in next release.
                throw new UnsupportedOperationException("Not implemented yet.");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Deliverable#getName()
             */
            @Override
            public Object getName() {
                return entity.getProperty("name");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Deliverable#getStatus()
             */
            @Override
            public Object getStatus() {
                return entity.getProperty("status");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Deliverable#getOwner()
             */
            @Override
            public Object getOwner() {
                return entity.getProperty("owner");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Deliverable#getQA()
             */
            @Override
            public Object getQA() {
                return entity.getProperty("qa");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Deliverable#getDueDate()
             */
            @Override
            public Object getDueDate() {
                return entity.getProperty("due");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Deliverable#getTextNotes()
             */
            @Override
            public Object getTextNotes() {
                return entity.getProperty("textNotes");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Item#getDescription()
             * @throws UnsupportedOperationException
             *             Not implemented yet.
             */
            @Override
            public String getDescription() {
                // TODO implement for support in next release.
                throw new UnsupportedOperationException("Not implemented yet.");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Item#getProperty(java.lang.String)
             * @throws UnsupportedOperationException
             *             Not implemented yet.
             */
            @Override
            public Object getProperty(String field) {
                // TODO implement for support in next release.
                throw new UnsupportedOperationException("Not implemented yet.");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Item#hasProperty(java.lang.String)
             * @throws UnsupportedOperationException
             *             Not implemented yet.
             */
            @Override
            public boolean hasProperty(String field) {
                // TODO implement for support in next release.
                throw new UnsupportedOperationException("Not implemented yet.");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Item#setProperty(java.lang.String,
             *      java.lang.Object)
             * @throws UnsupportedOperationException
             *             Not implemented yet.
             */
            @Override
            public Item setProperty(String field, Object info) {
                // TODO implement for support in next release.
                throw new UnsupportedOperationException("Not implemented yet.");
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.mediahouse.trac.model.Item#uid()
             * @throws UnsupportedOperationException
             *             Not implemented yet.
             */
            @Override
            public String uid() {
                // TODO implement for support in next release.
                throw new UnsupportedOperationException("Not implemented yet.");
            }
        }
    }

}
